package jointpart.assignment4;

import java.io.BufferedReader;
import java.io.FileReader;
import java.io.IOException;

import shared.*;


/**
 * Programming workshop assignment 4
 * 
 * Methods for creating hashtable for textfile and search for a given word:
 * 
 * The function hashMapFile reads all lines of a textfile (as arg[0]) and hashmaps it dynamic.
 * When the loadfactor is more than 0.75, the arraysize is doubled and all elements are remapped.
 * 
 * Unique words in textfiles:
 * small: 1376
 * medium: 16837
 * big: 59881
 * 
 */
public class Searcher4 {

	private static int hashMapSize = 200; // start value of hash map array size

	/**
	 * Searches for given word in HTMLWordList 
	 * 
	 * @param l HTMLWordList to search in
	 * @param w String word to search for
	 * @return Boolean true if word found
	 */
	public static boolean exists (WordList[] l, String w) {

		// Search for word in array

		String  word = w.toLowerCase();
		WordList currentWordList = l[hashKey(word)];

		// Iterate through HTMLWordList elements at key position
		while (currentWordList != null){

			if (currentWordList.str.equals(word)) { // word found
				return true;
			}

			currentWordList = currentWordList.next;
		}

		// Word not found
		return false;
	}

	/**
	 * Returns urls in which a given word appears 
	 * 
	 * @param l HTMLWordList to search in
	 * @param w String word to search for
	 * @return String with urls 
	 */
	public static String getUrls(WordList[] l, String w) {

		String word = w.toLowerCase().trim();


		// Search for word in array
		WordList currentWordList = l[hashKey(word)];
		

		// Iterate through HTMLWordList elements at key position
		while (currentWordList != null){

			if (currentWordList.str.equals(word)) { //word found

				return currentWordList.nexturl.toString();

			}

			currentWordList = currentWordList.next;
		}

		return null; // word not found
	}


	/**
	 * Reads in a textfile and hashmaps it.
	 * 
	 * @param filename
	 * @return HTMLWordList[] with linked list
	 * @throws IOException
	 */
	public static WordList[] hashMapFile(String filename, int initialSize) throws IOException {

		String name, currentUrl;
		
		hashMapSize = initialSize; // Initial size of hashtable

		WordList[] searchMap = new WordList[hashMapSize];

		int wordCount = 0;
		float loadFactor = 0;

		BufferedReader infile = new BufferedReader(new FileReader(filename));

		try {

			name = infile.readLine(); // Read first line of file											/* 1 */								
			name.startsWith("*PAGE"); // First line must be an url											/* 2 */											
			currentUrl = name.substring(6);  // Set variable currentUrl to first url						
			name = infile.readLine(); // Read next line 													/* 3 */

			// Iterate through lines until line is empty
			while (name != null) {																			/* 4 */

				if ( !(name.startsWith("*PAGE")) ) {  // line contains word (!url)							/* 5 */

					name = name.toLowerCase().trim(); // lowercase word
					int key = hashKey(name); // find hashKey
					WordList WordsAtKey = searchMap[key]; // look at first position

					// Check if words in map already
					if ( WordsAtKey == null ) {																/* 6 */

						// insert new HTMLWordList element with HTMLURLList element
						searchMap[key] = new WordList(name, new URLList(currentUrl, null) , null);
						wordCount++; // increase word counter

					} else { 																				

						// check if word already is in list 
						boolean match = false;																

						//Iterate through HTMLWordLists and check if word present
						while ( WordsAtKey != null) {														/* 7 */

							if ( !match && name.equals(WordsAtKey.str)) { //word present					/* 8 */

								// check if Url in words Urllist
								if (!WordsAtKey.nexturl.str.equals(currentUrl)) {									/* 9 */

									// Insert currentUrl as first HTLMURLList element in HTMLWordList
									WordsAtKey.nexturl = new URLList( currentUrl, WordsAtKey.nexturl);
								}

								match = true; // word was found in map
								break; // Since word was found there is no reason for further iteration
							}

							// If not the last element, go to next, else break
							if (WordsAtKey.next != null) {													/* 10 */

								WordsAtKey = WordsAtKey.next;

							} else {

								break;
							}

						}


						if (!match) { //last element and word not found										/* 11 */

							// add new HTMLWordList element with word to list

							WordsAtKey.next = new WordList(name, new URLList(currentUrl, null), null);
							wordCount++; // increase word counter
						}

					}

				}

				else {  // line contains Url

					currentUrl = name.substring(6);							
				}

				loadFactor = wordCount/ hashMapSize ;

				if (loadFactor > 0.75) {																	/* 12 */

					// HTMLWordList[] newMap = new HTMLWordList[2*hashMapSize];
					searchMap = resizeMap(searchMap, hashMapSize);

				}

				// read next line of file
				name = infile.readLine();
			}

		}

		catch (IOException exeption) {

			exeption.printStackTrace();
		}

		finally {

			infile.close();
		}

		return searchMap; 
	}	

	/*
	 * 
	 * For test:
	 * Other calculations of hashKey could be:
	 * 1. not to use Math.abs, instead use %(HASHMAP_SIZE/2) + HASHMAP_SIZE/2
	 *  - moves all the negative numbers to the first half of the array
	 * 
	 */
	/**
	 * hashKey generates a key for the hash map
	 * 
	 * @param word to generate the key from
	 * @return int hashKey based on word
	 * 
	 * */
	private static int hashKey(String word){

		//System.out.println(Math.abs(word.hashCode()) % hashMapSize);
		return Math.abs(word.hashCode()) % hashMapSize; //We use Math.abs because .hashcode returns negative number

	}


	/**
	 * resizeMap resizes the hashmap dynamically
	 * 
	 * @param map the hashmap to resize
	 * @return a resized map
	 */
	public static WordList[] resizeMap(WordList[] map, int oldSize) {

		//Increase value of variable for setting size 
		hashMapSize = 2*oldSize;

		//Create a new map
		WordList[] newMap = new WordList[hashMapSize];


		WordList currentHTMLWordList;
		int newKey;

		// Iterate through all HTMLWordList elements of map[] (old HTMLWordList)
		for (int i = 0;  i < map.length ; i++ ) {

			// set currentHTMLWordList to first element at [i]
			currentHTMLWordList = map[i]; 

			// Iterate through linked list of HTMLWordList elements at currentHTMLWordList and remap
			while (currentHTMLWordList != null) {

				// Calculate new key for element and add it to new hashmap
				newKey = hashKey(currentHTMLWordList.str);

				// If there already is an element add new element to linked list
				if (newMap[newKey] != null) {

					// add element in front of linked list
					newMap[newKey] = new WordList(currentHTMLWordList.str, currentHTMLWordList.nexturl, newMap[newKey]);

					// Else add element to first position of newMap
				} else {

					// Copy currentHTMLWordList to newMap
					newMap[newKey] = new WordList(currentHTMLWordList.str, currentHTMLWordList.nexturl, null);

				}

				// Go to next element
				currentHTMLWordList = currentHTMLWordList.next;		
			}

		}	

		return newMap;

	}

}


